﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/10 11:59:17
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using ImageK.Gui;
using ImageK.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.VisualStyles;
using ImageK.IO;
using ImageK.Java;
using ImageK.Measure;
using ImageK.Process;

namespace ImageK.Gui
{
    /** This class implements the Analyze/Plot Profile command.
    * @author Michael Schmid
    * @author Wayne Rasband
*/
    public class PlotWindow : ImageWindow , RoiListener
    {

	    private static  int WIDTH = 600;
        private static  int HEIGHT = 340;
        private static  int FONT_SIZE = 14;
        private static  String PREFS_WIDTH = "pp.width";
	    private static  String PREFS_HEIGHT = "pp.height";
	    private static  String PREFS_FONT_SIZE = "pp.fontsize";

	    /** @deprecated */
	    public static int CIRCLE = Plot.CIRCLE;
        /** @deprecated */
        public static int X = Plot.X;
        /** @deprecated */
        public static int BOX = Plot.BOX;
        /** @deprecated */
        public static int TRIANGLE = Plot.TRIANGLE;
        /** @deprecated */
        public static int CROSS = Plot.CROSS;
        /** @deprecated */
        public static int LINE = Plot.LINE;
        /** Write first X column when listing or saving. */
        public static bool saveXValues = true;
        /** Automatically close window after saving values. To set, use Edit/Options/Plots. */
        public static bool autoClose;
        /** Display the XY coordinates in a separate window. To set, use Edit/Options/Plots. */
        public static bool listValues;
        /** Interpolate line profiles. To set, use Edit/Options/Plots or setOption("InterpolateLines",boolean). */
        public static bool interpolate = true;
        // default values for new installations; values will be then saved in prefs
        private static int defaultFontSize = Prefs.getInt(PREFS_FONT_SIZE, FONT_SIZE);
        /** The width of the plot (without frame) in pixels. */
        public static int plotWidth = WIDTH;
        /** The height of the plot in pixels. */
        public static int plotHeight = HEIGHT;
        /** The plot text size, can be overridden by Plot.setFont, Plot.setFontSize, Plot.setXLabelFont etc. */
        public static int fontSize = defaultFontSize;
        /** Have axes with no grid lines. If both noGridLines and noTicks are true,
	     *	only min&max value of the axes are given */
        public static bool noGridLines;
        /** Have axes with no ticks. If both noGridLines and noTicks are true,
	     *	only min&max value of the axes are given */
        public static bool noTicks;

        private static String OPTIONS = "pp.options";
	    private static int SAVE_X_VALUES = 1;
        private static int AUTO_CLOSE = 2;
        private static int LIST_VALUES = 4;
        private static int INTERPOLATE = 8;
        private static int NO_GRID_LINES = 16;
        private static int NO_TICKS = 32;
        private static String moreButtonLabel = "More "+'\u00bb';
        private static String dataButtonLabel = "Data "+'\u00bb';

        bool wasActivated;           // true after window has been activated once, needed by PlotCanvas

        private Button list, data, more, live;
        private ContextMenuStrip dataPopupMenu, morePopupMenu;
        private static int NUM_MENU_ITEMS = 20; //how many menu items we have in total
        private ToolStripMenuItem[] menuItems = new ToolStripMenuItem[NUM_MENU_ITEMS];
        private Label statusLabel;
        private String userStatusText;
        private static String defaultDirectory = null;
        private static int options;
        private int defaultDigits = -1;
        private int markSize = 5;
        private static Plot staticPlot;
        private Plot? plot;

        private PlotMaker plotMaker;
        private ImagePlus srcImp;       // the source image for live plotting
        private Thread bgThread;        // thread for plotting (in the background)
        private bool doUpdate;       // tells the background thread to update

        private Roi[] rangeArrowRois;   // the overlays (arrows etc) for changing the range. Note: #10-15 must correspond to PlotDialog.dialogType!
        private bool rangeArrowsVisible;
        private int activeRangeArrow = -1;
        private static Color inactiveRangeArrowColor = Color.Gray;
        // private static Color inactiveRangeRectColor = new Color(0x20404040, true); //transparent gray
        private static Color activeRangeArrowColor = Color.Red;
        // private static Color activeRangeRectColor = new Color(0x18ff0000, true); //transparent red

        // static initializer
        static PlotWindow() {
		    options = Prefs.getInt(OPTIONS, SAVE_X_VALUES);
		    autoClose = (options&AUTO_CLOSE)!=0;
		    plotWidth = Prefs.getInt(PREFS_WIDTH, WIDTH);
		    plotHeight = Prefs.getInt(PREFS_HEIGHT, HEIGHT);
		    Size screen = IJ.getScreenSize();
		    if (plotWidth>screen.Width && plotHeight>screen.Height) {
			    plotWidth = WIDTH;
			    plotHeight = HEIGHT;
		    }
	    }

	    /**
	    * @deprecated
	    * replaced by the Plot class.
	    */
	    public PlotWindow(String title, String xLabel, String yLabel, float[] xValues, float[] yValues):base(createImage(title, xLabel, yLabel, xValues, yValues))
        {
            plot = staticPlot;
            ((PlotCanvas)getCanvas()).setPlot(plot);
        }

        /**
        * @deprecated
        * replaced by the Plot class.
        */
        public PlotWindow(String title, String xLabel, String yLabel, double[] xValues, double[] yValues):this(title, xLabel, yLabel, Tools.toFloat(xValues), Tools.toFloat(yValues))
        {
            
        }

        /** Creates a PlotWindow from a given ImagePlus with a Plot object.
         *  (called when reading an ImagePlus with an associated plot from a file) */
        public PlotWindow(ImagePlus imp, Plot plot):base(imp)
        {
            ((PlotCanvas)getCanvas()).setPlot(plot);
            this.plot = plot;
            draw();
        }

        /** Creates a PlotWindow from a Plot object. */
        public PlotWindow(Plot plot):base(plot.getImagePlus()) 
        {
            ((PlotCanvas)getCanvas()).setPlot(plot);
            this.plot = plot;
            draw();
        }

        /** Called by the constructor to generate the image the plot will be drawn on.
            This is a static method because constructors cannot call instance methods. */
        static ImagePlus createImage(String title, String xLabel, String yLabel, float[] xValues, float[] yValues)
        {
            staticPlot = new Plot(title, xLabel, yLabel, xValues, yValues);
            return new ImagePlus(title, staticPlot.getBlankProcessor());
        }

        /** Sets the x-axis and y-axis range.
         *  @deprecated use the corresponding method of the Plot class */
        public void setLimits(double xMin, double xMax, double yMin, double yMax)
        {
            plot.setLimits(xMin, xMax, yMin, yMax);
        }

        /** Adds a set of points to the plot or adds a curve if shape is set to LINE.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @param x			the x-coodinates
         *  @param y			the y-coodinates
         *  @param shape		Plot.CIRCLE, X, BOX, TRIANGLE, CROSS, LINE etc.
         *  @deprecated use the corresponding method of the Plot class */
        public void addPoints(float[] x, float[] y, int shape)
        {
            plot.addPoints(x, y, shape);
        }

        /** Adds a set of points to the plot using double arrays.
         *	Must be called before the plot is displayed.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void addPoints(double[] x, double[] y, int shape)
        {
            addPoints(Tools.toFloat(x), Tools.toFloat(y), shape);
        }

        /** Adds vertical error bars to the plot.
         *	Must be called before the plot is displayed.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void addErrorBars(float[] errorBars)
        {
            plot.addErrorBars(errorBars);
        }

        /** Draws a label.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void addLabel(double x, double y, String label)
        {
            plot.addLabel(x, y, label);
        }

        /** Changes the drawing color. The frame and labels are
         *	always drawn in black.
         *	Must be called before the plot is displayed.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void setColor(Color c)
        {
            plot.setColor(c);
        }

        /** Changes the line width.
         *	Must be called before the plot is displayed.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void setLineWidth(int lineWidth)
        {
            plot.setLineWidth(lineWidth);
        }

        /** Changes the font.
         *	Must be called before the plot is displayed.
         *	Note that there are more options available by using the methods of the Plot class instead.
         *  @deprecated use the corresponding method of the Plot class */
        public void changeFont(Font font)
        {
            plot.changeFont(font);
        }

        /** Displays the plot. */
        public void draw()
        {
            FlowLayoutPanel bottomPanel = new FlowLayoutPanel();
            bottomPanel.FlowDirection = FlowDirection.LeftToRight;
            bottomPanel.Dock = DockStyle.Bottom;
            bottomPanel.Height = 50;
            int hgap = IJ.isMacOSX() ? 1 : 5;

            list = new Button()
            {
                Text = " List ",
                Height = 35
            };
            list.Click += actionPerformed;
            // list.addActionListener(this);
            bottomPanel.Controls.Add(list);
            // bottomPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, hgap, 0));
            data = new Button()
            {
                Text = dataButtonLabel,
                Height = 35
            };
            data.Click += actionPerformed;
            // data.addActionListener(this);
            bottomPanel.Controls.Add(data);
            more = new Button()
            {
                Text = moreButtonLabel,
                Height = 35
            };
            more.Click += actionPerformed;
            // more.addActionListener(this);
            bottomPanel.Controls.Add(more);
            if (plot!=null && plot.getPlotMaker()!=null)
            {
                live = new Button()
                {
                    Text = "Live",
                    Height = 35
                };
                // live.addActionListener(this);
                live.Click += actionPerformed;
                bottomPanel.Controls.Add(live);
            }
            statusLabel = new Label();
            // statusLabel.setFont(new Font("Monospaced", Font.PLAIN, 12));
            // statusLabel.setBackground(new Color(220, 220, 220));
            bottomPanel.Controls.Add(statusLabel);
            add(bottomPanel);
            // data.add(getDataPopupMenu());
            // more.add(getMorePopupMenu());
            plot.draw();
           //  LayoutManager lm = getLayout();
           //  if (lm is ImageLayout)
			        // ((ImageLayout)lm).ignoreNonImageWidths(true);  //don't expand size to make the panel fit
            GUI.scale(bottomPanel);
            maximizeCoordinatesLabelWidth();
            this.pack();

            ImageProcessor ip = plot.getProcessor();
            bool ipIsColor = ip is ColorProcessor;
            bool impIsColor = imp.getProcessor() is ColorProcessor;
            if (ipIsColor != impIsColor)
                imp.setProcessor(null, ip);
            else
                imp.updateAndDraw();
            //todo:
            // if (listValues)
            //     showList(/*useLabels=*/false);
            // else
            //     ic.requestFocus();  //have focus on the canvas, not the button, so that pressing the space bar allows panning
            // if (Prefs.autoLivePlots && bgThread==null)
            //     enableLivePlot();
	    }

        protected override void pack()
        {
            this.Height += 30;
        }

        /** Sets the Plot object shown in this PlotWindow. Does not update the window. */
	    public void setPlot(Plot plot)
        {
            this.plot = plot;
            ((PlotCanvas)getCanvas()).setPlot(plot);
        }

        /** Releases the resources used by this PlotWindow */
        public void dispose()
        {
            // if (plot!=null)
            //     plot.dispose();
            // disableLivePlot();
            // plot = null;
            // plotMaker = null;
            // srcImp = null;
            // this.dispose();
        }

        /** Called when the window is activated (WindowListener)
         *  Window layout is finished at latest a few millisec after windowActivated, then the
         *  'wasActivated' boolean is set to tell the ImageCanvas that resize events should
         *  lead to resizing the canvas (before, creating the layout can lead to resize events)*/
        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            if (!wasActivated)
            {
                new Task(()=>
                {
                    IJ.wait(50);  //sometimes, window layout is done only a few millisec after windowActivated
                    wasActivated = true;
                }).Start();
            }
        }


        /** Called when the canvas is resized */
        void canvasResized()
        {
            if (plot == null) return;
            /*Dimension d1 = getExtraSize();
            Dimension d2 = plot.getMinimumSize();
            setMinimumSize(new Dimension(d1.width + d2.width, d1.height + d2.height));*/
            maximizeCoordinatesLabelWidth();
        }

        /** Maximizes the width for the coordinate&status readout field and its parent bottomPanel */
        void maximizeCoordinatesLabelWidth()
        {
            //todo:
            // Insets insets = getInsets();                    //by default, left & right insets are 0 anyhow
            // Component parent = statusLabel.getParent();     //the bottomPanel, has insets of 0
            // if (!parent.isValid()) parent.validate();
            // int cWidth = getWidth() - 2*HGAP - statusLabel.getX() - insets.left - insets.right;
            // int cHeight = statusLabel.getPreferredSize().height;
            // statusLabel.setPreferredSize(new Dimension(cWidth, cHeight));
            // parent.setSize(getWidth() - 2*HGAP, parent.getHeight());
        }

        /** Shows the text in the coordinate&status readout field at the bottom.
         *  This text may get temporarily replaced for 'tooltips' (mouse over range arrows etc.).
         *  Call with a null argument to enable coordinate readout again. */
        public void showStatus(String text)
        {
            userStatusText = text;
            if (statusLabel != null)
                statusLabel.Text = (text == null ? "" : text);
        }

        /** Names for popupMenu items. Update NUM_MENU_ITEMS at the top when adding new ones! */
        private static int SAVE = 0, COPY = 1, COPY_ALL = 2, LIST_SIMPLE = 3, ADD_FROM_TABLE = 4, ADD_FROM_PLOT = 5, ADD_FIT = 6, //data menu
                SET_RANGE = 7, PREV_RANGE = 8, RESET_RANGE = 9, FIT_RANGE = 10,  //the rest is in the more menu
                ZOOM_SELECTION = 11, AXIS_OPTIONS = 12, LEGEND = 13, STYLE = 14, TEMPLATE = 15, RESET_PLOT = 16,
                FREEZE = 17, HI_RESOLUTION = 18, PROFILE_PLOT_OPTIONS = 19;
        //the following commands are disabled when the plot is frozen
        private static int[] DISABLED_WHEN_FROZEN = new int[]{ADD_FROM_TABLE, ADD_FROM_PLOT, ADD_FIT,
                    SET_RANGE, PREV_RANGE, RESET_RANGE, FIT_RANGE, ZOOM_SELECTION, AXIS_OPTIONS, LEGEND, STYLE, RESET_PLOT};

        /** Prepares and returns the popupMenu of the Data>> button */
        // ContextMenuStrip getDataPopupMenu()
        // {
        //     dataPopupMenu = new ContextMenuStrip();
        //     // GUI.scalePopupMenu(dataPopupMenu);
        //     menuItems[SAVE] = addPopupItem(dataPopupMenu, "Save Data...");
        //     menuItems[COPY] = addPopupItem(dataPopupMenu, "Copy 1st Data Set");
        //     menuItems[COPY_ALL] = addPopupItem(dataPopupMenu, "Copy All Data");
        //     menuItems[LIST_SIMPLE] = addPopupItem(dataPopupMenu, "List (Simple Headings)");
        //     dataPopupMenu.addSeparator();
        //     menuItems[ADD_FROM_TABLE] = addPopupItem(dataPopupMenu, "Add from Table...");
        //     menuItems[ADD_FROM_PLOT] = addPopupItem(dataPopupMenu, "Add from Plot...");
        //     menuItems[ADD_FIT] = addPopupItem(dataPopupMenu, "Add Fit...");
        //     return dataPopupMenu;
        // }

        // /** Prepares and returns the popupMenu of the More>> button */
        // PopupMenu getMorePopupMenu()
        // {
        //     morePopupMenu = new PopupMenu();
        //     GUI.scalePopupMenu(morePopupMenu);
        //     menuItems[SET_RANGE] = addPopupItem(morePopupMenu, "Set Range...");
        //     menuItems[PREV_RANGE] = addPopupItem(morePopupMenu, "Previous Range");
        //     menuItems[RESET_RANGE] = addPopupItem(morePopupMenu, "Reset Range");
        //     menuItems[FIT_RANGE] = addPopupItem(morePopupMenu, "Set Range to Fit All");
        //     menuItems[ZOOM_SELECTION] = addPopupItem(morePopupMenu, "Zoom to Selection");
        //     morePopupMenu.addSeparator();
        //     menuItems[AXIS_OPTIONS] = addPopupItem(morePopupMenu, "Axis Options...");
        //     menuItems[LEGEND] = addPopupItem(morePopupMenu, "Legend...");
        //     menuItems[STYLE] = addPopupItem(morePopupMenu, "Contents Style...");
        //     menuItems[TEMPLATE] = addPopupItem(morePopupMenu, "Use Template...");
        //     menuItems[RESET_PLOT] = addPopupItem(morePopupMenu, "Reset Format");
        //     menuItems[FREEZE] = addPopupItem(morePopupMenu, "Freeze Plot", true);
        //     menuItems[HI_RESOLUTION] = addPopupItem(morePopupMenu, "High-Resolution Plot...");
        //     morePopupMenu.addSeparator();
        //     menuItems[PROFILE_PLOT_OPTIONS] = addPopupItem(morePopupMenu, "Plot Defaults...");
        //     return morePopupMenu;
        // }

        // MenuItem addPopupItem(PopupMenu popupMenu, String s)
        // {
        //     return addPopupItem(popupMenu, s, false);
        // }
        //
        // MenuItem addPopupItem(PopupMenu popupMenu, String s, boolean isCheckboxItem)
        // {
        //     MenuItem mi = null;
        //     if (isCheckboxItem)
        //     {
        //         mi = new CheckboxMenuItem(s);
        //         ((CheckboxMenuItem)mi).addItemListener(this);
        //     }
        //     else
        //     {
        //         mi = new MenuItem(s);
        //         mi.addActionListener(this);
        //     }
        //     popupMenu.add(mi);
        //     return mi;
        // }

        /** Called if user has activated a button or popup menu item */
        public void actionPerformed(object? sender, EventArgs e)
        {
            try
            {
                Object b = sender;
                if (b==live)
                    toggleLiveProfiling();
                else if (b==list)
                    showList(/*useLabels=*/true);
                // else if (b==data)
                // {
                //     enableDisableMenuItems();
                //     dataPopupMenu.show((Component)b, 1, 1);
                // }
                // else if (b==more)
                // {
                //     enableDisableMenuItems();
                //     morePopupMenu.show((Component)b, 1, 1);
                // }
                // else if (b==menuItems[SAVE])
                //     saveAsText();
                // else if (b==menuItems[COPY])
                //     copyToClipboard(false);
                // else if (b==menuItems[COPY_ALL])
                //     copyToClipboard(true);
                // else if (b==menuItems[LIST_SIMPLE])
                //     showList(/*useLabels=*/false);
                // else if (b==menuItems[ADD_FROM_TABLE])
                //     new PlotContentsDialog(plot, PlotContentsDialog.ADD_FROM_TABLE).showDialog(this);
                // else if (b==menuItems[ADD_FROM_PLOT])
                //     new PlotContentsDialog(plot, PlotContentsDialog.ADD_FROM_PLOT).showDialog(this);
                // else if (b==menuItems[ADD_FIT])
                //     new PlotContentsDialog(plot, PlotContentsDialog.ADD_FIT).showDialog(this);
                // else if (b==menuItems[ZOOM_SELECTION])
                // {
                //     if (imp!=null && imp.getRoi()!=null && imp.getRoi().isArea())
                //         plot.zoomToRect(imp.getRoi().getBounds());
                // }
                // else if (b==menuItems[SET_RANGE])
                //     new PlotDialog(plot, PlotDialog.SET_RANGE).showDialog(this);
                // else if (b==menuItems[PREV_RANGE])
                //     plot.setPreviousMinMax();
                // else if (b==menuItems[RESET_RANGE])
                //     plot.setLimitsToDefaults(true);
                // else if (b==menuItems[FIT_RANGE])
                //     plot.setLimitsToFit(true);
                // else if (b==menuItems[AXIS_OPTIONS])
                //     new PlotDialog(plot, PlotDialog.AXIS_OPTIONS).showDialog(this);
                // else if (b==menuItems[LEGEND])
                //     new PlotDialog(plot, PlotDialog.LEGEND).showDialog(this);
                // else if (b==menuItems[STYLE])
                //     new PlotContentsDialog(plot, PlotContentsDialog.STYLE).showDialog(this);
                // else if (b==menuItems[TEMPLATE])
                //     new PlotDialog(plot, PlotDialog.TEMPLATE).showDialog(this);
                // else if (b==menuItems[RESET_PLOT])
                // {
                //     plot.setFont(Font.PLAIN, fontSize);
                //     plot.setAxisLabelFont(Font.PLAIN, fontSize);
                //     plot.setFormatFlags(Plot.getDefaultFlags());
                //     plot.setFrameSize(plotWidth, plotHeight); //updates the image only when size changed
                //     plot.updateImage();
                // }
                // else if (b==menuItems[HI_RESOLUTION])
                //     new PlotDialog(plot, PlotDialog.HI_RESOLUTION).showDialog(this);
                // else if (b==menuItems[PROFILE_PLOT_OPTIONS])
                //     IJ.doCommand("Plots...");
                // ic.requestFocus();  //have focus on the canvas, not the button, so that pressing the space bar allows panning
            }
            catch (Exception ex) { IJ.handleException(ex); }
        }

     //    private void enableDisableMenuItems()
     //    {
     //        bool frozen = plot.isFrozen();   //prepare menu according to 'frozen' state of plot
     //        ((CheckboxMenuItem)menuItems[FREEZE]).setState(frozen);
     //        for (int i : DISABLED_WHEN_FROZEN)
			  //       menuItems[i].setEnabled(!frozen);
     //        if (!PlotContentsDialog.tableWindowExists())
     //            menuItems[ADD_FROM_TABLE].setEnabled(false);
     //        if (plot.getDataObjectDesignations().length == 0)
     //            menuItems[ADD_FIT].setEnabled(false);
	    // }

	    // /** Called if the user activates/deactivates a CheckboxMenuItem */
	    // public void itemStateChanged(ItemEvent e)
     //    {
     //        if (e.getSource()==menuItems[FREEZE])
     //        {
     //            boolean frozen = ((CheckboxMenuItem)menuItems[FREEZE]).getState();
     //            plot.setFrozen(frozen);
     //        }
     //    }

    // /**
    //  * Updates the X and Y values when the mouse is moved and, if appropriate,
    //  * shows/hides the overlay with the triangular buttons for changing the axis
    //  * range limits.
    //  * Overrides mouseMoved() in ImageWindow.
    //  *
    //  * @see ij.gui.ImageWindow#mouseMoved
    //  */
    // public void mouseMoved(int x, int y)
    // {
    //     super.mouseMoved(x, y);
    //     if (plot == null)
    //         return;
    //     String statusText = null; //coordinate readout, status or tooltip, will be shown in coordinate&status line
    //
    //     //arrows and other symbols for modifying the plot range
    //     if (x < plot.leftMargin || y > plot.topMargin + plot.frameHeight)
    //     {
    //         if (!rangeArrowsVisible && !plot.isFrozen())
    //             showRangeArrows();
    //         if (activeRangeArrow < 0)       //mouse is not on one of the symbols, ignore (nothing to display)
    //         { }
    //         else if (activeRangeArrow < 8)  //mouse over an arrow: 0,3,4,7 for increase, 1,2,5,6 for decrease
    //             statusText = ((activeRangeArrow+1)&0x02) != 0 ? "Decrease Range" : "Increase Range";
    //         else if (activeRangeArrow == 8) //it's the 'R' icon
    //             statusText = "Reset Range";
    //         else if (activeRangeArrow == 9) //it's the 'F' icon
    //             statusText = "Full Range (Fit All)";
    //         else if (activeRangeArrow >= 10 &&
    //                 activeRangeArrow < 14)  //space between arrow-pairs for single number
    //             statusText = "Set limit...";
    //         else if (activeRangeArrow >= 14)
    //             statusText = "Axis Range & Options...";
    //         boolean repaint = false;
    //         if (activeRangeArrow >= 0 && !rangeArrowRois[activeRangeArrow].contains(x, y))
    //         {
    //             rangeArrowRois[activeRangeArrow].setFillColor(
    //                     activeRangeArrow < 10 ? inactiveRangeArrowColor : inactiveRangeRectColor);
    //             repaint = true;             //de-highlight arrow where cursor has moved out
    //             activeRangeArrow = -1;
    //         }
    //         if (activeRangeArrow < 0)
    //         {     //no currently highlighted arrow, do we have a new one?
    //             int i = getRangeArrowIndex(x, y);
    //             if (i >= 0)
    //             {               //we have an arrow or symbol at cursor position
    //                 rangeArrowRois[i].setFillColor(
    //                         i < 14 ? activeRangeArrowColor : activeRangeRectColor);
    //                 activeRangeArrow = i;
    //                 repaint = true;
    //             }
    //         }
    //         if (repaint) ic.repaint();
    //     }
    //     else if (rangeArrowsVisible)
    //         hideRangeArrows();
    //
    //     if (statusText == null)
    //         statusText = userStatusText != null ? userStatusText : plot.getCoordinates(x, y);
    //     if (statusLabel != null)
    //         statusLabel.setText(statusText);
    // }

    // /** Called by PlotCanvas */
    // void mouseExited(MouseEvent e)
    // {
    //     if (rangeArrowsVisible)
    //         hideRangeArrows();
    // }
    //
    // /** Mouse wheel: zooms when shift or ctrl is pressed, scrolls in x if space bar down, in y otherwise. */
    // public void mouseWheelMoved(MouseWheelEvent e)
    // {
//         if (plot.isFrozen() || !(ic instanceof PlotCanvas)) 
//         {      //frozen plots are like normal images
//             base.mouseWheelMoved(e);
//             return;
//         }
// int rotation = e.getWheelRotation();
// int amount = e.getScrollAmount();
// if (e.getX() < plot.leftMargin || e.getX() > plot.leftMargin + plot.frameWidth)//n__
//     return;
// if (e.getY() < plot.topMargin || e.getY() > plot.topMargin + plot.frameHeight)
//     return;
// boolean ctrl = (e.getModifiers()&Event.CTRL_MASK)!=0;
// if (amount<1) amount=1;
// if (rotation==0)
//     return;
// if (ctrl||IJ.shiftKeyDown())
// {
//     double zoomFactor = rotation<0 ? Math.pow(2, 0.2) : Math.pow(0.5, 0.2);
//     Point loc = ic.getCursorLoc();
//     int x = ic.screenX(loc.x);
//     int y = ic.screenY(loc.y);
//     ((PlotCanvas)ic).zoom(x, y, zoomFactor);
// }
// else if (IJ.spaceBarDown())
//     plot.scroll(rotation*amount*Math.max(ic.imageWidth/50, 1), 0);
// else
//     plot.scroll(0, rotation*amount*Math.max(ic.imageHeight/50, 1));
// 	}

    /**
     * Creates an overlay with triangular buttons and othr symbols for changing the axis range
     * limits and shows it
     */
    void showRangeArrows()
{
//     if (imp == null)
//         return;
//     hideRangeArrows(); //in case we have old arrows from a different plot size or so
//     rangeArrowRois = new Roi[4 * 2 + 2 + 4 + 2]; //4 arrows per axis, + 'Reset' and 'Fit All' icons, + 4 numerical input boxes + 2 axes
//     int i = 0;
//     int height = imp.getHeight();
//     int arrowH = plot.topMargin < 14 ? 6 : 8; //height of arrows and distance between them; base is twice that value
//     float[] yP = new float[] { height - arrowH / 2, height - 3 * arrowH / 2, height - 5 * arrowH / 2 - 0.1f };
//
//     for (float x : new float[] { plot.leftMargin, plot.leftMargin + plot.frameWidth }) { //create arrows for x axis
//     float[] x0 = new float[] { x - arrowH / 2, x - 3 * arrowH / 2 - 0.1f, x - arrowH / 2 };
//     rangeArrowRois[i++] = new PolygonRoi(x0, yP, 3, Roi.POLYGON);
//     float[] x1 = new float[] { x + arrowH / 2, x + 3 * arrowH / 2 + 0.1f, x + arrowH / 2 };
//     rangeArrowRois[i++] = new PolygonRoi(x1, yP, 3, Roi.POLYGON);
// }
// float[] xP = new float[] { arrowH / 2 - 0.1f, 3 * arrowH / 2, 5 * arrowH / 2 + 0.1f };
// for (float y : new float[] { plot.topMargin + plot.frameHeight, plot.topMargin })
// { //create arrows for y axis
//     float[] y0 = new float[] { y + arrowH / 2, y + 3 * arrowH / 2 + 0.1f, y + arrowH / 2 };
//     rangeArrowRois[i++] = new PolygonRoi(xP, y0, 3, Roi.POLYGON);
//     float[] y1 = new float[] { y - arrowH / 2, y - 3 * arrowH / 2 - 0.1f, y - arrowH / 2 };
//     rangeArrowRois[i++] = new PolygonRoi(xP, y1, 3, Roi.POLYGON);
// }
// Font theFont = new Font("SansSerif", Font.BOLD, 13);
//
// TextRoi txtRoi = new TextRoi(1, height - 19, "\u2009R\u2009", theFont);  //thin spaces to make roi slightly wider
// rangeArrowRois[8] = txtRoi;
// TextRoi txtRoi2 = new TextRoi(20, height - 19, "\u2009F\u2009", theFont);
// rangeArrowRois[9] = txtRoi2;
//
// rangeArrowRois[10] = new Roi(plot.leftMargin - arrowH/2 + 1, height - 5 * arrowH / 2, arrowH - 2, arrowH * 2);//numerical box left
// rangeArrowRois[11] = new Roi(plot.leftMargin + plot.frameWidth - arrowH/2 + 1, height - 5 * arrowH / 2, arrowH - 2, arrowH * 2);//numerical box right
// rangeArrowRois[12] = new Roi(arrowH / 2, plot.topMargin + plot.frameHeight - arrowH/2 + 1, arrowH * 2, arrowH -2);//numerical box bottom
// rangeArrowRois[13] = new Roi(arrowH / 2, plot.topMargin - arrowH/2 + 1, arrowH * 2, arrowH - 2);//numerical box top
//
// int topMargin = plot.topMargin;
// int bottomMargin = topMargin + plot.frameHeight;
// int leftMargin = plot.leftMargin;
// int rightMargin = plot.leftMargin + plot.frameWidth;
// rangeArrowRois[14] = new Roi(leftMargin, bottomMargin+2,        // area to click for x axis options
//         rightMargin - leftMargin + 1, 2*arrowH);
// rangeArrowRois[15] = new Roi(leftMargin-2*arrowH-2, topMargin,  // area to click for y axis options
//         2*arrowH, bottomMargin - topMargin + 1);
//
// Overlay ovly = imp.getOverlay();
// if (ovly == null)
//     ovly = new Overlay();
// for (Roi roi : rangeArrowRois)
// {
//     if (roi instanceof PolygonRoi)
//                    roi.setFillColor(inactiveRangeArrowColor);
//
//             else if (roi instanceof TextRoi) {
//     roi.setStrokeColor(Color.WHITE);
//     roi.setFillColor(inactiveRangeArrowColor);
// } else
//     roi.setFillColor(inactiveRangeRectColor); //transparent gray for single number boxes and axis range
// ovly.add(roi);
//         }
//         imp.setOverlay(ovly);
// ic.repaint();
// rangeArrowsVisible = true;
     }

        void hideRangeArrows()
        {
            //todo:
        //     if (imp == null || rangeArrowRois==null) return;
        //     Overlay ovly = imp.getOverlay();
        //     if (ovly == null) return;
        //     for (Roi roi : rangeArrowRois)
			     //    ovly.remove(roi);
        // ic.repaint();
        // rangeArrowsVisible = false;
        // activeRangeArrow = -1;
        }

	    /** Returns the index of the range-modifying symbol or axis at the
	     *  cursor position x,y, or -1 of none.
	     *  Index numbers for arrows start with 0 at the 'down' arrow of the
	     *  lower side of the x axis and end with 7 the up arrow at the upper
	     *  side of the y axis. Numbers 8 & 9 are for "Reset Range" and "Fit All";
	     *  numbers 10-13 for a dialog to set a single limit, and 14-15 for the axis options. */

	    int getRangeArrowIndex(int x, int y)
        {
            if (!rangeArrowsVisible) return -1;
            for (int i = 0; i<rangeArrowRois.Length; i++)
                if (rangeArrowRois[i].getBounds().Contains(x, y))
                    return i;
            return -1;
        }


        /** Shows the data of the backing plot in a Textwindow with columns */
        void showList(bool useLabels)
        {
            ResultsTable rt = plot.getResultsTable(saveXValues, useLabels);
            if (rt==null) return;
            rt.show("Plot Values");
            if (autoClose)
            {
                imp.changes=false;
                close();
            }
        }

        /** Returns the plot values with simple headings (X, Y, Y1 etc, not the labels) as a ResultsTable.
         *  Use plot.getResultsTableWithLabels for a table with data set labels as column headings */
        public ResultsTable getResultsTable()
        {
            return plot.getResultsTable(saveXValues);
        }

        /** creates the data that fills the showList() function values */
        private String getValuesAsString()
        {
            ResultsTable rt = getResultsTable();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i<rt.size(); i++)
            {
                sb.Append(rt.getRowAsString(i));
                sb.Append("\n");
            }
            return sb.ToString();
        }

        /** Saves the data of the plot in a text file */
        void saveAsText()
        {
            //todo:
            // if (plot.getXValues() == null)
            // {
            //     IJ.error("Plot has no data");
            //     return;
            // }
            // SaveDialog sd = new SaveDialog("Save as Text", "Values", Prefs.defaultResultsExtension());
            // String name = sd.getFileName();
            // if (name==null) return;
            // String directory = sd.getDirectory();
            // IJ.wait(250);  // give system time to redraw ImageJ window
            // IJ.ShowStatus("Saving plot values...");
            // ResultsTable rt = plot.getResultsTable(/*writeFirstXColumn=*/saveXValues, /*useLabels=*/true);
            // try
            // {
            //     rt.saveAs(directory+name);
            // }
            // catch (IOException e)
            // {
            //     IJ.error("" + e);
            //     return;
            // }
            // if (autoClose)
            // { imp.changes=false; close(); }
        }

        // /** Copy the first dataset or all values to the clipboard */
        // void copyToClipboard(bool writeAllColumns)
        // {
        //     float[] xValues = plot.getXValues();
        //     float[] yValues = plot.getYValues();
        //     if (xValues == null) return;
        //     Clipboard systemClipboard = null;
        //     try { systemClipboard = getToolkit().getSystemClipboard(); }
        //     catch (Exception e) { systemClipboard = null; }
        //     if (systemClipboard==null)
        //     { IJ.error("Unable to copy to Clipboard."); return; }
        //     IJ.showStatus("Copying plot values...");
        //     CharArrayWriter aw = new CharArrayWriter(10*xValues.length);
        //     PrintWriter pw = new PrintWriter(aw); //uses platform's line termination characters
        //
        //     if (writeAllColumns)
        //     {
        //         ResultsTable rt = plot.getResultsTableWithLabels();
        //         if (!Prefs.dontSaveHeaders)
        //         {
        //             String headings = rt.getColumnHeadings();
        //             pw.println(headings);
        //         }
        //         for (int i = 0; i<rt.size(); i++)
        //             pw.println(rt.getRowAsString(i));
        //     }
        //     else
        //     {
        //         int xdigits = 0;
        //         if (saveXValues)
        //             xdigits = plot.getPrecision(xValues);
        //         int ydigits = plot.getPrecision(yValues);
        //         for (int i = 0; i<Math.min(xValues.length, yValues.length); i++)
        //         {
        //             if (saveXValues)
        //                 pw.println(IJ.d2s(xValues[i], xdigits)+"\t"+IJ.d2s(yValues[i], ydigits));
        //             else
        //                 pw.println(IJ.d2s(yValues[i], ydigits));
        //         }
        //     }
        //     String text = aw.toString();
        //     pw.close();
        //     StringSelection contents = new StringSelection(text);
        //     systemClipboard.setContents(contents, this);
        //     IJ.showStatus(text.length() + " characters copied to Clipboard");
        //     if (autoClose)
        //     { imp.changes=false; close(); }
        // }

        // public void lostOwnership(Clipboard clipboard, Transferable contents) { }

        public float[] getXValues()
        {
            return plot.getXValues();
        }

        public float[] getYValues()
        {
            return plot.getYValues();
        }

        // /** Draws a new plot in this window. */
        // public void drawPlot(Plot plot)
        // {
        //     this.plot = plot;
        //     if (imp!=null)
        //     {
        //         if (ic instanceof PlotCanvas)
				    //     ((PlotCanvas)ic).setPlot(plot);
        // imp.setProcessor(null, plot.getProcessor());
        // plot.setImagePlus(imp); //also adjusts the calibration of imp
		      //   }
	       //  }

	    /** Called once when ImageJ quits. */
	    public static void savePreferences(Dictionary<string,string> prefs)
        {
            // double min = ProfilePlot.getFixedMin();
            // double max = ProfilePlot.getFixedMax();
            // prefs.put(PREFS_WIDTH, Integer.toString(plotWidth));
            // prefs.put(PREFS_HEIGHT, Integer.toString(plotHeight));
            // prefs.put(PREFS_FONT_SIZE, Integer.toString(defaultFontSize));
            // int options = 0;
            // if (!interpolate) options |= INTERPOLATE; // true=0, false=1
            // prefs.put(OPTIONS, Integer.toString(options));
        }

        private void toggleLiveProfiling()
        {
            bool liveMode = bgThread != null;
            // if (liveMode)
            //     disableLivePlot();
            // else
            //     enableLivePlot();
        }

        // /* Enable live plotting.
        //  * This requires that the PlotWindow has been initialized with a Plot having a PlotMaker */
        // private void enableLivePlot()
        // {
        //     if (plotMaker==null)
        //         plotMaker = plot!=null ? plot.getPlotMaker() : null;
        //     if (plotMaker==null) return;
        //     srcImp = plotMaker.getSourceImage();
        //     if (srcImp==null)
        //         return;
        //     if (bgThread==null)
        //     {
        //         bgThread = new Thread(this, "Live Plot");
        //         bgThread.setPriority(Math.max(bgThread.getPriority()-3, Thread.MIN_PRIORITY));
        //         doUpdate = true;
        //         bgThread.start();
        //     }
        //     if (IJ.debugMode) IJ.log("PlotWindow.createListeners");
        //     ImagePlus.addImageListener(this);
        //     Roi.addRoiListener(this);
        //     Font font = live.getFont();
        //     live.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
        //     live.setForeground(Color.red);
        // }

        // private void disableLivePlot()
        // {
        //     if (IJ.debugMode) IJ.log("PlotWindow.disableLivePlot: "+srcImp);
        //     if (srcImp==null)
        //         return;
        //     if (bgThread!=null)
        //         bgThread.interrupt();
        //     bgThread = null;
        //     ImagePlus.removeImageListener(this);
        //     Roi.removeRoiListener(this);
        //     if (live != null)
        //     {
        //         Font font = live.getFont();
        //         live.setFont(new Font(font.getName(), Font.PLAIN, font.getSize()));
        //         live.setForeground(Color.black);
        //     }
        // }


        /** For live plots, update the plot if the ROI of the source image changes */
        public void roiModified(ImagePlus img, int id)
        {
            if (IJ.debugMode) IJ.log("PlotWindow.roiModified: "+img+"  "+id);
            if (img==srcImp)
            {
                doUpdate=true;
                // notify();
            }
        }

        // Unused
        public void imageOpened(ImagePlus imp)
        {
        }

        /** For live plots, this method is called if the source image content is changed. */
        // public void imageUpdated(ImagePlus imp)
        // {
        //     if (imp==srcImp)
        //     {
        //         doUpdate = true;
        //         notify();
        //     }
        // }

        // /** For live plots, if either the source image or this image are closed, exit live mode */
        // public void imageClosed(ImagePlus imp)
        // {
        //     if (imp==srcImp || imp==this.imp)
        //     {
        //         disableLivePlot();
        //         srcImp = null;
        //         plotMaker = null;
        //     }
        // }

        // the background thread for live plotting.
        public void run()
        {
            while (true)
            {
                IJ.wait(50);    //delay to make sure the roi has been updated
                // Plot plot = plotMaker!=null ? plotMaker.getPlot() : null;
                // if (doUpdate && plot!=null && plot.getNumPlotObjects()>0)
                // {
                //     plot.useTemplate(this.plot, this.plot.templateFlags | Plot.COPY_SIZE | Plot.COPY_LABELS | Plot.COPY_AXIS_STYLE |
                //             Plot.COPY_CONTENTS_STYLE | Plot.COPY_LEGEND | Plot.COPY_EXTRA_OBJECTS);
                //     plot.setPlotMaker(plotMaker);
                //     this.plot = plot;
                //     ((PlotCanvas)ic).setPlot(plot);
                //     ImageProcessor ip = plot.getProcessor();
                //     if (ip!=null && imp!=null)
                //     {
                //         imp.setProcessor(null, ip);
                //         plot.setImagePlus(imp);
                //     }
                // }
                // lock(this) {
                //     if (doUpdate)
                //     {
                //         doUpdate = false;       //and loop again
                //     }
                //     else
                //     {
                //         try { wait(); } //notify wakes up the thread
                //         catch (InterruptedException e)
                //         { //interrupted tells the thread to exit
                //             return;
                //         }
                //     }
                // }
            }
        }

        /** Returns the Plot associated with this PlotWindow. */
        public Plot getPlot()
        {
            return plot;
        }

        /** Freezes the active plot window, so the image does not get redrawn for zooming,
         *  setting the range, etc. */
        public static void freeze()
        {
       //      VisualStyleElement.Window win = WindowManager.getActiveWindow();
       //      if (win!=null && (win is PlotWindow))
			    // ((PlotWindow)win).getPlot().setFrozen(true);
	    }
	
        public static void setDefaultFontSize(int size)
        {
            if (size < 9) size = 9;
            defaultFontSize = size;
        }

        public static int getDefaultFontSize()
        {
            return defaultFontSize;
        }

    }

}
